﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Agr.CQRS.Infrastructure.ServiceBus;
using Agr.CQRS.Infrastructure.Orm;
using System.Timers;
using log4net;

namespace Agr.CQRS.Infrastructure.Events
{
    public class DomainEventWatcher : IDisposable
    {
        private static ILog Logger = LogManager.GetLogger(typeof(DomainEventWatcher));

        OrmContext ormContext;

        public DomainEventWatcher(OrmContext ctx)
        {
            ormContext = ctx;
        }

        private Timer tmr;
        private const int timerInterval = 1000;
        internal string pollerName;
        private const string defaultPollerName = "DEFAULT";

        public void Start(string pollerName)
        {
            Logger.WarnFormat("DomainEventWatcher started for {0}", pollerName);
            
            if (string.IsNullOrEmpty(pollerName))
                this.pollerName = defaultPollerName;
            else
                this.pollerName = pollerName;

            tmr = new Timer(timerInterval);
            tmr.Elapsed += tmr_Elapsed;
            tmr.Start();
        }

        public void Stop()
        {
            Dispose();
        }

        void tmr_Elapsed(object sender, ElapsedEventArgs e)
        {
            tmr.Stop();

            //TODO: this can be done much more efficiently: each poller now
            // polls the database seperately. 
            using (var sit = ormContext.OpenSession())
            {
                var pointer = sit.UseIn<QueryGetPointer>().ForPoller(pollerName).Result();
                if (pointer == null)
                {
                    pointer = new DomainEventPointer { PollerName = pollerName };
                    sit.Insert(pointer);
                }

                var sdevents = sit.UseIn<QueryGetEventsFromId>().FromAndWithout(pointer.LastSeenId).Results();

                if (sdevents.Count > 0)
                {
                    Logger.InfoFormat("New events found ({0})", sdevents.Count);

                    pointer.LastSeenId = sdevents.Last().Id;
                    process(sdevents);
                }
                sit.Commit();
            }

            tmr.Start();
        }

        public event EventHandler<NewDomainEventsReceivedEventArgs> NewDomainEventsReceived;
        private void process(IList<StoredDomainEvent> sdevents)
        {
            if (NewDomainEventsReceived == null)
            {
                Logger.WarnFormat("No event listeners for poller {0}", pollerName);
                return;
            }

            var events = from sde in sdevents select sde.GetEvent();
            NewDomainEventsReceived.Invoke(this, new NewDomainEventsReceivedEventArgs(events));
        }

        public void Dispose()
        {
            if (tmr != null)
                tmr.Dispose();
            tmr = null;
        }
    }
}
